#include <linux/module.h>
#include <linux/init.h>

#include <linux/fs.h>	/* for inode, file & file_operations */
#include <linux/cdev.h> /* for cdev */
#include <linux/types.h>	/* for dev_t */
#include <asm/uaccess.h>	/* for copy_to_user */
#include <linux/slab.h>		/* for kmalloc */
#include <linux/device.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Serval Li");

dev_t dev_one;
struct cdev cdev_one;
struct class *one_class;

int one_open(struct inode *inode, struct file *filp)
{
	printk(KERN_INFO "Open /dev/one!\n");
	return 0;	
}

int one_release(struct inode *inode, struct file *filp)
{
	printk(KERN_INFO "Close /dev/one!\n");
	return 0;
}

ssize_t one_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	char *pff = kmalloc(size, GFP_KERNEL);

	memset(pff, 0xFF, size);
	copy_to_user(buf, pff, size);

	kfree(pff);

	return size;
}

ssize_t one_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	return size;	/* reference drivers/char/mem.c */
}
/* /dev/one supports read only, write is fake */
struct file_operations one_ops = {
	.owner = THIS_MODULE,
	.open = one_open,
	.release = one_release,
	.read = one_read,
	.write = one_write,
};

static int one_init(void)
{
	int ret;

	dev_one = MKDEV(248, 0);
	unregister_chrdev_region(dev_one, 1);
	ret = register_chrdev_region(dev_one, 1, "dev-one");
	if(ret < 0) {
		printk(KERN_ERR "register_chrdev_region() fails\n");
		return ret;
	}

	cdev_init(&cdev_one, &one_ops);
	cdev_one.owner = THIS_MODULE;
	cdev_one.ops = &one_ops;

	cdev_add(&cdev_one, dev_one, 1);

	one_class = class_create(THIS_MODULE, "dev-one");
	device_create(one_class, NULL, dev_one, NULL, "one");

	printk(KERN_INFO "load /dev/one!\n");
	return 0;
}

static void one_exit(void)
{
	cdev_del(&cdev_one);
	device_destroy(one_class, dev_one);
	class_destroy(one_class);
	unregister_chrdev_region(dev_one, 1);

	printk(KERN_INFO "unload /dev/one!\n");
	return;
}

module_init(one_init);
module_exit(one_exit);

